
package com.haiyou.common.util.string;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;

import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.haiyou.common.util.collect.ArrayUtils;
import com.haiyou.common.util.object.ClassUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class StringUtils {

	public static final Charset UTF8 = Charset.forName("UTF-8");

	public static final CharsetEncoder UTF8_ENCODER = UTF8.newEncoder();

	public static final CharsetDecoder UTF8_DECODER = UTF8.newDecoder();

	/***
	 * 下划线命名转为驼峰命名
	 *
	 * @param underlineString 下划线命名的字符串
	 */

	public static String underlineToHump(String underlineString) {
		StringBuilder result = new StringBuilder();
		String ss[] = underlineString.split("_");
		for (String s : ss) {
			if (result.length() == 0) {
				result.append(s.toLowerCase());
			} else {
				result.append(s.substring(0, 1).toUpperCase());
				result.append(s.substring(1).toLowerCase());
			}
		}
		return result.toString();
	}

	/***
	 * 驼峰命名转为下划线命名
	 *
	 * @param humpString 驼峰命名的字符串
	 */

	public static String humpToUnderline(String humpString) {
		StringBuilder sb = new StringBuilder(humpString);
		int temp = 0;// 偏移量，第i个下划线的位置是 当前的位置+ 偏移量（i-1）,第一个下划线偏移量是0
		for (int i = 0; i < humpString.length(); i++) {
			if (Character.isUpperCase(humpString.charAt(i))) {
				sb.insert(i + temp, "_");
				temp += 1;
			}
		}
		return sb.toString().toLowerCase();
	}

	/**
	 * 字符类型
	 * 
	 * @author ljs
	 *
	 */
	static public enum Type {

		/**
		 * 未知
		 */
		UNKNOWN(-1),

		/**
		 * 中文
		 */
		TYPE_CHINESE(1 << 0),

		/**
		 * 英文
		 */
		TYPE_ENGLISH(1 << 1),

		/**
		 * 数字
		 */
		TYPE_NUMBER(1 << 2),

		/**
		 * 符号
		 */
		TYPE_SYMBOL(1 << 3),

		/**
		 * 大写英文
		 */
		TYPE_LAGER_ENGLISH(1 << 4),

		/**
		 * 小写英文
		 */
		TYPE_SMALL_ENGLISH(1 << 5),

		/**
		 * 英文+数字
		 */
		TYPE_ENGLISH_NUMBER(TYPE_ENGLISH.value | TYPE_NUMBER.value),

		/**
		 * 中文+英文+数字
		 */
		TYPE_CHINESE_ENFLISH_NUMBER(TYPE_CHINESE.value | TYPE_ENGLISH.value | TYPE_NUMBER.value),

		;

		public final int value;

		private Type(int value) {
			this.value = value;
		}

	}

	/**
	 * 通用的分隔符
	 */
	public static final String SPLITTER = "-";

	/**
	 * 通过通用的分隔符连接
	 * 
	 * @param objects
	 * @return
	 */
	public static String merge(Object... objects) {
		if (objects == null || objects.length < 1)
			throw new RuntimeException();
		return mergeJoiner(SPLITTER, objects);
	}

	/**
	 * 通过通用的分隔符还原
	 * 
	 * @param s
	 * @return
	 */
	public static String[] parse(String s) {
		if (isEmpty(s))
			throw new RuntimeException();
		return parseJoiner(SPLITTER, s);
	}

	/**
	 * 通过指定的的分隔符连接
	 * 
	 * @param joiner  指定的连接符
	 * @param objects
	 * @return
	 */
	public static String mergeJoiner(String joiner, Object... objects) {
		if (isEmpty(joiner)) {
			throw new RuntimeException("连接符joiner不可以为空:" + joiner);
		}

		if (!StringUtils.stringContainIt(joiner, Type.TYPE_SYMBOL)) {
			throw new RuntimeException("连接符joiner必须包含符号:" + joiner);
		}

		if (objects == null || objects.length < 1)
			throw new RuntimeException();

		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < objects.length; i++) {
			Object obj = objects[i];
			sb.append(obj.toString());
			if (i < objects.length - 1) {
				sb.append(joiner);
			}
		}
		return sb.toString();
	}

	/**
	 * 通过指定的的分隔符还原
	 * 
	 * @param joiner
	 * @param s
	 * @return
	 */
	public static String[] parseJoiner(String joiner, String s) {
		if (isEmpty(joiner) || isEmpty(s))
			throw new RuntimeException();
		return s.split(joiner);
	}

	/**
	 * 数字类型:科学计数法,整数,小数
	 * 
	 * @author Administrator
	 */
	public enum NumberType {
		/**
		 * 科学计数法
		 */
		E_NUMBER,
		/**
		 * 带小数的科学计数法
		 */
		E_POINT,
		/**
		 * 整数
		 */
		INTEGER_NUMBER,
		/**
		 * 带小数的整数
		 */
		INTEGER_POINT,
	}

	/**
	 * 获取当前堆栈信息
	 * 
	 * @param t
	 * @return
	 */
	public static List<String> getStackTrace(Throwable t) {

		LinkedList<String> list = Lists.newLinkedList();
		list.add(Thread.currentThread().getName());
		StackTraceElement[] stackTraceElements = t.getStackTrace();
		for (int i = 0; i < stackTraceElements.length; i++) {
			StackTraceElement s = stackTraceElements[i];
			String format = "%s.%s(%d)";
			String m = String.format(format, s.getClassName(), s.getMethodName(), s.getLineNumber());
			list.add(m);
		}
		return list;
	}

	/**
	 * 获取所有异常堆栈信息
	 * 
	 * @param t
	 * @return
	 */
	public static List<String> getSuppressed(Throwable t) {

		LinkedList<String> list = Lists.newLinkedList();
		Throwable[] throwables = t.getSuppressed();
		for (int i = 0; i < throwables.length; i++) {
			Throwable e = throwables[i];
			list.addAll(getStackTrace(e));
		}
		return list;
	}

	/**
	 * 方法1：java自带的函数
	 * 
	 * @param str 传入的字符串
	 * @return 是整数返回true,否则返回false
	 */
	public static boolean isNumeric1(String str) {

		for (int i = str.length(); --i >= 0;) {
			if (!Character.isDigit(str.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 方法二：推荐，速度最快 判断是否为整数
	 * 
	 * @param str 传入的字符串
	 * @return 是整数返回true,否则返回false
	 */
	public static boolean isNumeric2(String str) {

		Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
		return pattern.matcher(str).matches();
	}

	/**
	 * 方法3：类似方法2
	 * 
	 * @param str 传入的字符串
	 * @return 是整数返回true,否则返回false
	 */
	public static boolean isNumeric3(String str) {

		Pattern pattern = Pattern.compile("[0-9]*");
		return pattern.matcher(str).matches();
	}

	/**
	 * 方法4：
	 * 
	 * @param str 传入的字符串
	 * @return 是整数返回true,否则返回false
	 */
	public final static boolean isNumeric4(String s) {

		if (s != null && !"".equals(s.trim()))
			return s.matches("^[0-9]*$");
		else
			return false;
	}

	/**
	 * 方法5：用ascii码
	 * 
	 * @param str 传入的字符串
	 * @return 是整数返回true,否则返回false
	 */
	public static boolean isNumeric5(String str) {

		for (int i = str.length(); --i >= 0;) {
			int chr = str.charAt(i);
			if (chr < 48 || chr > 57)
				return false;
		}
		return true;
	}

	/**
	 * 根据数据的字符串表达式,解析数据类型
	 * 
	 * @param number
	 * @return
	 */
	public static NumberType parseNumberType(String number) {

		if (number.indexOf('E') > 0) {
			if (number.indexOf('.') > 0) {
				return NumberType.E_POINT;
			}
			return NumberType.E_NUMBER;
		}
		if (number.indexOf('.') > 0) {
			return NumberType.INTEGER_POINT;
		}
		return NumberType.INTEGER_NUMBER;
	}

	public static byte[] string2Bytes(String string, Charset charset) {
		Objects.requireNonNull(string);
		if (isEmpty(string)) {
			byte[] array = new byte[1];
			array[0] = '\0';
			return array;
		}
		byte[] b = string.getBytes(charset);
		byte[] array = new byte[b.length + 1];
		for (int i = 0; i < b.length; i++) {
			array[i] = b[i];
		}
		array[array.length - 1] = '\0';
		return array;
	}

	public static byte[] string2Bytes(String string) {
		return string2Bytes(string, UTF8);
	}

	public static String bytes2String(byte[] array, Charset charset) {
		Objects.requireNonNull(array);
		int offset = 0;
		int dest = 0;
		for (int i = 0; i < array.length - offset; i++) {
			if (array[offset + i] == '\0') {
				dest = i;
				break;
			}
		}
		return new String(array, offset, dest, charset);
	}

	public static String bytes2String(byte[] array) {
		return bytes2String(array, UTF8);
	}

	public static boolean isEmpty(String str) {

		return str == null || str.isEmpty();
	}

	/**
	 * 去掉字符串所有的空格
	 * 
	 * @param str
	 * @return
	 */
	public static String replaceEmpty(String str) {

		return str.replaceAll(" ", "").trim();
	}

	/**
	 * 倒转字符串
	 * 
	 * @param str
	 * @return
	 */
	public static String reversed(String str) {

		StringBuilder builder = new StringBuilder(str.length());
		for (int i = str.length(); --i >= 0;) {
			builder.append(str.charAt(i));
		}
		return builder.toString();
	}

	/**
	 * 二分法倒转,效率更高
	 * 
	 * @param str
	 * @return
	 */
	public static String reversed2(String str) {
		return new String(ArrayUtils.reversed2(str.toCharArray()));
	}

	public static boolean isAllSymbol(String str) {
		ArrayList<Type> types = getCharTypeList(str);
		for (Type t : types) {
			if (t != Type.TYPE_SYMBOL)
				return false;
		}
		return true;
	}

	public static boolean isAllEnglish(String str) {

		for (int i = 0; i < str.length(); i++) {
			if (!(str.charAt(i) >= 'A' && str.charAt(i) <= 'Z') && !(str.charAt(i) >= 'a' && str.charAt(i) <= 'z')) {
				return false;
			}
		}
		return true;
	}

	public static boolean isAllNumber(String str) {

		for (int i = 0; i < str.length(); i++) {
			if (!(str.charAt(i) >= '0' && str.charAt(i) <= '9')) {
				return false;
			}
		}
		return true;
	}

	public static boolean isAllChinese(String str) {

		for (int i = 0; i < str.length(); i++) {
			if (!(str.charAt(i) >= 0x4e00 && str.charAt(i) <= 0x9faf)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * str中是否不包含type类型的字符
	 * 
	 * @param str
	 * @param type
	 * @see {@link Type}
	 * @return
	 */
	public static boolean stringNotContainIt(String str, Type type) {

		int charType = Type.UNKNOWN.value;
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if (c >= 0x4E00 && c <= 0x9FAF) {
				charType &= ~Type.TYPE_CHINESE.value;
			} else if (c >= '0' && c <= '9') {
				charType &= ~Type.TYPE_NUMBER.value;
			} else if (c >= 'a' && c <= 'z') {
				charType &= ~Type.TYPE_SMALL_ENGLISH.value;
			} else if (c >= 'A' && c <= 'Z') {
				charType &= ~Type.TYPE_LAGER_ENGLISH.value;
			} else {
				charType &= ~Type.TYPE_SYMBOL.value;
			}
		}
		return (charType & type.value) != 0;
	}

	/**
	 * str中是否包含type类型的字符
	 * 
	 * @param str
	 * @param type
	 * @see {@link Type}
	 * @return
	 */
	public static boolean stringContainIt(String str, Type type) {

		Type charType = Type.UNKNOWN;
		int size = str.length();
		for (int i = 0; i < size; i++) {
			char c = str.charAt(i);
			if (c >= 0x4E00 && c <= 0x9FAF) {
				charType = Type.TYPE_CHINESE;
			} else if (c >= '0' && c <= '9') {
				charType = Type.TYPE_NUMBER;
			} else if (c >= 'a' && c <= 'z') {
				charType = Type.TYPE_SMALL_ENGLISH;
			} else if (c >= 'A' && c <= 'Z') {
				charType = Type.TYPE_LAGER_ENGLISH;
			} else {
				charType = Type.TYPE_SYMBOL;
			}
			if (charType == type) {
				return true;
			}
		}
		return false;
	}

	public static ArrayList<Type> getCharTypeList(String str) {

		int len = str.length();
		ArrayList<Type> list = Lists.newArrayListWithCapacity(len);
		for (int i = 0; i < len; i++) {
			char c = str.charAt(i);
			if (c >= 0x4e00 && c <= 0x9faf) {
				list.add(Type.TYPE_CHINESE);
			} else if (c >= 'A' && c <= 'Z') {
				list.add(Type.TYPE_LAGER_ENGLISH);
			} else if (c >= 'a' && c <= 'z') {
				list.add(Type.TYPE_SMALL_ENGLISH);
			} else if (c >= '0' && c <= '9') {
				list.add(Type.TYPE_NUMBER);
			} else {
				list.add(Type.TYPE_SYMBOL);
			}
		}
		return list;
	}

	public static boolean isSymbol(char c) {
		if (c >= 0x4e00 && c <= 0x9faf || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9') {
			return false;
		} else {
			return true;
		}
	}

	public static String toString(byte[] buffer) {

		return Arrays.toString(buffer);
	}

	public static String toString(short[] buffer) {

		return Arrays.toString(buffer);
	}

	public static String toString(int[] buffer) {

		return Arrays.toString(buffer);
	}

	public static String toString(long[] buffer) {

		return Arrays.toString(buffer);
	}

	public static String toString(float[] buffer) {

		return Arrays.toString(buffer);
	}

	public static String toString(double[] buffer) {

		return Arrays.toString(buffer);
	}

	public static String toString(boolean[] buffer) {

		return Arrays.toString(buffer);
	}

	public static String toString(char[] buffer) {

		return Arrays.toString(buffer);
	}

	public static String toString(String[] arr) {

		return Arrays.toString(arr);
	}

	public static String toString(Object[] arr) {

		try {
			return Arrays.toString(arr);
		} catch (Exception e) {
			return e.getMessage();
		}
	}

	public static String deepToString(Object[] arr) {

		try {
			return Arrays.deepToString(arr);
		} catch (Exception e) {
			return e.getMessage();
		}
	}

	/**
	 * 把对象转String,仅仅提供可视化需求,并不能反序列化,默认使用fastjson规则
	 * 
	 * @param obj 必须是标准的bean 所有字段必须有setter/getter 方法,并且必须有无参构造器
	 *            </p>
	 *            如果不是bean,那么fastjson序列化可能会出现异常导致失败
	 *            </p>
	 *            如果失败并且抛出异常就使用toString(Object object, boolean
	 *            showSuperClassField)重新转string
	 */
	public static String toString(Object obj) {
		if (Objects.isNull(obj))
			return "null";
		try {
			return JSON.toJSONString(obj);
		} catch (Exception e) {
			try {
				return toString(obj, true);
			} catch (Exception ex) {
				return ex.getLocalizedMessage();
			}
		}
	}

	/**
	 * 对象转string,没有实现toString的成员变量对象会显示identityHashCode的值
	 * 
	 * @param obj
	 * @param mappedSuperclass 是否包含父类的字段
	 * @return
	 */
	public static String toString(Object obj, boolean mappedSuperclass) {
		Set<Field> fields = Sets.newLinkedHashSet();
		ClassUtils.findAllDeclaredFields(mappedSuperclass, obj.getClass(), fields);
		fields.removeIf(field -> Modifier.isStatic(field.getModifiers()));
		StringBuilder sb = new StringBuilder();
		int i = 0;
		for (Field field : fields) {
			try {
				String name = field.getName();
				Object value = field.get(obj);
				sb.append(name).append("=").append(value == null ? "null" : value);
				if (i++ < fields.size() - 1)
					sb.append(",");
			} catch (Exception e) {
				log.error(e.getMessage() + ":" + field.toGenericString() + "," + mappedSuperclass, e);
			}
		}
		return obj.getClass().getSimpleName() + "(" + sb.toString() + ")";
	}

	/**
	 * double保留2位小数,带四舍五入但结果不精确
	 * 
	 * @param v
	 * @return
	 */
	public static double double2(double v) {
		double d = v;
		d = (double) Math.round(d * 100) / 100;// 四舍五入
		return d;
	}

	/**
	 * double保留2位小数,带四舍五入结果精确
	 * 
	 * @param v
	 * @return
	 */
	public static double double2d(double v) {
		double d = v;
		BigDecimal b = new BigDecimal(d);
		d = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); // 四舍五入
		return d;
	}

	/**
	 * double保留2位小数,不带四舍五入返回字符串小数
	 * 
	 * @param v
	 * @return
	 */
	public static String double2s(double v) {
		double d = v;
		DecimalFormat df = new DecimalFormat("#.00");// 不四舍五入
		String str = df.format(d);
		return str;
	}

	/**
	 * double保留2位小数,不带四舍五入返回字符串小数
	 * 
	 * @param v
	 * @return
	 */
	public static String double2f(double v) {
		double d = v;
		String str = String.format("%.2f", d);// 不四舍五入
		return str;
	}

	/**
	 * double保留2位小数,带四舍五入但不精确,返回字符串小数
	 * 
	 * @param v
	 * @return
	 */
	public static String double2r(double v) {
		double d = v;
		NumberFormat nf = NumberFormat.getNumberInstance();
		nf.setMaximumFractionDigits(2);
		nf.setRoundingMode(RoundingMode.HALF_UP);
		String str = nf.format(d);
		return str;
	}

	/**
	 * 将double类型的科学计数法转换成指定格式的正常字符串,保留两位小数点
	 * 
	 * @param d
	 * @return
	 */
	public static String doubleByScientificCountingToString(double d) {

		return doubleByScientificCountingToString(d, 2);
	}

	/**
	 * @param d将double类型的科学计数法转换成指定格式的正常字符串
	 * @param decimalPoint小数点位数
	 * @return
	 */
	public static String doubleByScientificCountingToString(double d, int decimalPoint) {

		StringBuilder formater = new StringBuilder().append("#,##0.");
		for (int i = 0; i < decimalPoint; i++) {
			formater.append(0);
		}
		DecimalFormat a = new DecimalFormat(formater.toString());
		String frmStr = a.format(d);
		return frmStr;
	}

	/**
	 * double类型的科学计数法转换成指定格式的正常字符串后再还原成long
	 * 
	 * @param d
	 * @param decimalPoint
	 * @return
	 */
	public static long doubleScientificCountingToLong(double d, int decimalPoint) {

		return Long.parseLong(doubleByScientificCountingToString(d, decimalPoint).replaceAll(",", "").split("\\.")[0]);
	}

	/**
	 * double类型的科学计数法转换成指定格式的正常字符串后再还原成long,保留两位小数点
	 * 
	 * @param d
	 * @return
	 */
	public static long doubleScientificCountingToLong(double d) {

		return Long.parseLong(doubleByScientificCountingToString(d, 2).replaceAll(",", "").split("\\.")[0]);
	}

	public static String nonNull(String msg) {

		return msg == null ? "" : msg;
	}

	/**
	 * 二进制转换unicode
	 */
	public static String bs2u(byte[] bytes) {
		StringBuffer unicode = new StringBuffer();
		unicode.append('[');
		for (int i = 0; i < bytes.length; i++) {
			unicode.append(s2u(String.valueOf(bytes[i])));
			if (i < bytes.length - 1) {
				unicode.append(',');
			} else {
				unicode.append(']');
			}
		}
		return unicode.toString();
	}

	/**
	 * 字符串转换unicode
	 */
	public static String s2u(String string) {

		StringBuffer unicode = new StringBuffer();

		for (int i = 0; i < string.length(); i++) {

			// 取出每一个字符
			char c = string.charAt(i);

			// 转换为unicode
			unicode.append(String.format("\\u%04x", Integer.valueOf(c)));
		}

		return unicode.toString();
	}

	/**
	 * unicode 转字符串
	 */
	public static String u2s(String unicode) {

		StringBuffer string = new StringBuffer();

		String[] hex = unicode.split("\\\\u");

		for (int i = 1; i < hex.length; i++) {

			// 转换出每一个代码点
			int data = Integer.parseInt(hex[i], 16);

			// 追加成string
			string.append((char) data);
		}

		return string.toString();
	}

	// 將10進制轉換為16進制
	public static String hex10To16(Integer numb) {
		return "0x" + String.format("%08x", numb);// %X就是格式化成十六进制(X就是大写，x就是小写)，8表示8位，0表示如果不够8位则往前面补0。
	}

	// 將10進制轉換為16進制
	public static String simpleHex10To16(Integer numb) {
		String hex = Integer.toHexString(numb);
		return "0x" + hex;
	}

	// 將16進制字符串轉換為10進制數字
	public static int hex16To10(String hexs) {
		if (hexs.toLowerCase().startsWith("0x"))
			hexs = hexs.substring(2);
		BigInteger bigint = new BigInteger(hexs, 16);
		int numb = bigint.intValue();
		return numb;
	}

	public static void main(String[] args) {
		int numb = 9999;
		String hex = hex10To16(numb);
		String simpleHex = simpleHex10To16(numb);
		System.out.println("  9999  的16进制为" + hex);
		System.out.println("  9999  的16进制为" + simpleHex);
		System.out.println("  16進制字符 " + hex + " 的10進制數字為   " + hex16To10(hex));

	}
}
